
#include <winsock2.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

#define DEFAULT_PORT	5019

int client_send(void* msg);
int client_receive(void* msg);

// secure message
char* secure(char pwd[100]) {
	// encrypt pwd
	char encrypt_pwd[128] = { '\0' };
	int i;
	int count = strlen(pwd);

	for (i = 0; i < count; i++)
	{
		/* encryption */
		encrypt_pwd[i] = pwd[i] + i + 5;
	}

	encrypt_pwd[i] = '\0';
	char* return_pwd = (char*)malloc(sizeof(char*) * 100);
	strcpy(return_pwd, encrypt_pwd);
	return return_pwd;
}

// decoding message
char* re_secure(char pwd[100]) {
	// decoding msg
	char re_pwd[128] = { '\0' };
	int i;
	int count = strlen(pwd);

	for (i = 0; i < count; ++i)
	{
		/* decoding */
		re_pwd[i] = pwd[i] - i - 5;
	}

	re_pwd[i] = '\0';
	char* return_pwd = (char*)malloc(sizeof(char*) * 100);
	strcpy(return_pwd, re_pwd);
	return return_pwd;
}

void askForLog(SOCKET connect_sock) {

	bool is_log = false;
	while (is_log == false) {
		// Add your user name
		char name_Buff[100];
		int name_len;
		printf("Please input your user name:\n");
		gets(name_Buff);
		// Send to server
		name_len = send(connect_sock, name_Buff, sizeof(name_Buff), 0);
		if (name_len == SOCKET_ERROR) {
			fprintf(stderr, "send name failed with error %d\n", WSAGetLastError());
			WSACleanup();
			return -1;
		}
		if (name_len == 0) {
			printf("server closed connection\n");
			closesocket(connect_sock);
			WSACleanup();
			return -1;
		}
		// Add your password
		char psd_Buff[100];
		int psd_len;
		printf("Please input your password:\n");
		gets(psd_Buff);
		// Send to server
		psd_len = send(connect_sock, psd_Buff, sizeof(psd_Buff), 0);
		if (psd_len == SOCKET_ERROR) {
			fprintf(stderr, "send password failed with error %d\n", WSAGetLastError());
			WSACleanup();
			return -1;
		}
		if (psd_len == 0) {
			printf("server closed connection\n");
			closesocket(connect_sock);
			WSACleanup();
			return -1;
		}
		// Wait for server confirm
		printf("Waiting for server feedback...\n");
		char fb_Buff[100];
		int fb_len;
		fb_len = recv(connect_sock, fb_Buff, sizeof(fb_Buff), 0);
		if (fb_len == SOCKET_ERROR) {
			fprintf(stderr, "recv feedback failed with error %d\n", WSAGetLastError());
			closesocket(connect_sock);
			WSACleanup();
			return -1;
		}
		if (fb_len == 0) {
			printf("server closed connection\n");
			closesocket(connect_sock);
			WSACleanup();
			return -1;
		}
		// Get confirm
		if (strcmp(fb_Buff, "True") == 0) {
			printf("Log success\n");
			is_log = true;
		}
		else {
			printf("Please try again\n");
			is_log = false;
		}
	}

}

int chatMode(SOCKET connect_sock) {
	int mode_len;
	char input_mode[10];
	printf("Please input mode:\n");
	gets(input_mode);
	mode_len = send(connect_sock, input_mode, sizeof(input_mode), 0);
	if (mode_len == SOCKET_ERROR) {
		fprintf(stderr, "send mode failed with error %d\n", WSAGetLastError());
		WSACleanup();
		return -1;
	}
	if (mode_len == 0) {
		printf("server closed connection\n");
		closesocket(connect_sock);
		WSACleanup();
		return -1;
	}

	if (strcmp(input_mode, "0") == 0) {
		return 0;
	}
	else {
		return 1;
	}
}

void getUsrList(SOCKET connect_sock) {
	char list_Buff[100];
	int list_len;
	printf("Receiving user list:\n");
	list_len = recv(connect_sock, list_Buff, sizeof(list_Buff), 0);
	if (list_len == SOCKET_ERROR) {
		fprintf(stderr, "recv usr list failed with error %d\n", WSAGetLastError());
		closesocket(connect_sock);
		WSACleanup();
		return -1;
	}
	if (list_len == 0) {
		printf("server closed connection\n");
		closesocket(connect_sock);
		WSACleanup();
		return -1;
	}
	// Output to screen
	printf("Please select one usr you want to chat with:\n");
	printf("%s\n", list_Buff);

	// chat friend selecting
	char select_usr[5];
	int select_len;
	gets(select_usr);
	select_len = send(connect_sock, select_usr, sizeof(select_usr), 0);
	if (select_len == SOCKET_ERROR) {
		fprintf(stderr, "send usr list failed with error %d\n", WSAGetLastError());
		closesocket(connect_sock);
		WSACleanup();
		return -1;
	}
	if (select_len == 0) {
		printf("server closed connection\n");
		closesocket(connect_sock);
		WSACleanup();
		return -1;
	}


}

int main(int argc, char** argv) {

	//int addr_len;
	struct sockaddr_in server_addr;
	struct hostent* hp;
	SOCKET connect_sock;
	WSADATA wsaData;

	char* server_name = "localhost";
	unsigned short	port = DEFAULT_PORT;
	unsigned int	addr;

	if (argc != 3) {
		printf("echoscln [server name] [port number]\n");
		// return -1;
	}
	else {
		server_name = argv[1];
		port = atoi(argv[2]);
	}

	if (WSAStartup(0x202, &wsaData) == SOCKET_ERROR) {
		// stderr: standard error are printed to the screen.
		fprintf(stderr, "WSAStartup failed with error %d\n", WSAGetLastError());
		//WSACleanup function terminates use of the Windows Sockets DLL. 
		WSACleanup();
		return -1;
	}

	if (isalpha(server_name[0]))
		hp = gethostbyname(server_name);
	else {
		addr = inet_addr(server_name);
		hp = gethostbyaddr((char*)&addr, 4, AF_INET);
	}

	if (hp == NULL)
	{
		fprintf(stderr, "Cannot resolve address: %d\n", WSAGetLastError());
		WSACleanup();
		return -1;
	}

	//copy the resolved information into the sockaddr_in structure
	memset(&server_addr, 0, sizeof(server_addr));
	memcpy(&(server_addr.sin_addr), hp->h_addr, hp->h_length);
	server_addr.sin_family = hp->h_addrtype;
	server_addr.sin_port = htons(port);

	connect_sock = socket(AF_INET, SOCK_STREAM, 0);	//TCp socket

	if (connect_sock == INVALID_SOCKET) {
		fprintf(stderr, "socket() failed with error %d\n", WSAGetLastError());
		WSACleanup();
		return -1;
	}

	printf("Client connecting to: %s\n", hp->h_name);

	if (connect(connect_sock, (struct sockaddr*)&server_addr, sizeof(server_addr))
		== SOCKET_ERROR) {
		fprintf(stderr, "connect() failed with error %d\n", WSAGetLastError());
		WSACleanup();
		return -1;
	}

	// **************** Start Coding Here **************** //

	// Ask for connecting
	askForLog(connect_sock);

	// Select chatting mode
	int chat_mode = chatMode(connect_sock);

	// If it's single mode, receive online user list
	if (chat_mode == 0) {
		getUsrList(connect_sock);
	}

	unsigned thread_send;
	unsigned thread_receive;

	// Create sender thread
	if (_beginthreadex(NULL, 0, &client_send, (void*)connect_sock, 0, &thread_send) == 0) {
		fprintf(stderr, "thread create failed with error %d\n", WSAGetLastError());
		WSACleanup();
		return -1;
	}
	// Create receiver thread
	if (_beginthreadex(NULL, 0, &client_receive, (void*)connect_sock, 0, &thread_receive) == 0) {
		fprintf(stderr, "thread create failed with error %d\n", WSAGetLastError());
		WSACleanup();
		return -1;
	}

	printf("Connected Bulid\n");
	// Wait for sub threads
	while (1) {

	}

}

int client_send(void* msg) {

	SOCKET connect_sock = (SOCKET*)msg;

	while (1) {
		char szBuff_pre[100];
		char szBuff[100];
		int msg_len;

		printf("Please input your message:\n");
		gets(szBuff_pre);

		// secure message
		strcpy(szBuff, secure(szBuff_pre));
		msg_len = send(connect_sock, szBuff_pre, sizeof(szBuff), 0);


		if (msg_len == SOCKET_ERROR) {
			fprintf(stderr, "send() failed with error %d\n", WSAGetLastError());
			WSACleanup();
			return -1;
		}

		if (msg_len == 0) {
			printf("server closed connection\n");
			closesocket(connect_sock);
			WSACleanup();
			return -1;
		}

	}
	closesocket(connect_sock);
	WSACleanup();
	return 0;
}

int client_receive(void* msg) {

	SOCKET connect_sock = (SOCKET*)msg;

	while (1) {
		char szBuff_pre[100];
		char szBuff[100];
		int msg_len;

		msg_len = recv(connect_sock, szBuff, sizeof(szBuff), 0);
		// decoding message
		strcpy(szBuff_pre, re_secure(szBuff));

		if (msg_len == SOCKET_ERROR) {
			fprintf(stderr, "recv() failed with error %d\n", WSAGetLastError());
			WSACleanup();
			return -1;
		}

		if (msg_len == 0) {
			printf("server closed connection\n");
			closesocket(connect_sock);
			WSACleanup();
			return -1;
		}
		// Receive message
		printf("%s\n", szBuff);

	}
	closesocket(connect_sock);
	WSACleanup();
	return 0;
}